Skip to content

fix: marketplace install auth host on *.ghe.com (closes #1285)#1292

Open
edenfunf wants to merge 3 commits into
microsoft:mainfrom
edenfunf:fix/marketplace-ghe-host-prefix-1285
Open

fix: marketplace install auth host on *.ghe.com (closes #1285)#1292
edenfunf wants to merge 3 commits into
microsoft:mainfrom
edenfunf:fix/marketplace-ghe-host-prefix-1285

Conversation

@edenfunf
Copy link
Copy Markdown
Contributor

TL;DR

For marketplaces registered on *.ghe.com (GHE Cloud) hosts, plugin install resolved auth against github.com instead of the registered enterprise host. This change backfills the marketplace host on the resolver's canonical string so DependencyReference.parse recovers the correct host downstream. Closes #1285.

Problem

apm install <plugin>@<marketplace> against a marketplace registered with --host corp.ghe.com failed with an authentication error. The verbose trace showed:

Resolving my-plugin@my-marketplace via marketplace...
Resolved to: myorg/my-marketplace/plugins/my-plugin        <-- missing corp.ghe.com/ prefix
Auth resolved: host=github.com, org=myorg, ...             <-- wrong host
[i] plugins/my-plugin/apm.yml@main (Authentication failed for myorg/my-marketplace ...)

DependencyReference.parse defaults missing hosts to github.com, so every *.ghe.com marketplace mis-routes auth. The same plugin installed via fully-qualified path worked correctly:

apm install corp.ghe.com/myorg/my-marketplace/plugins/my-plugin

i.e. the rest of the install pipeline already handles GHE Cloud correctly; only the marketplace → canonical step dropped the host.

A second, quieter symptom: the bare canonical also produced an apm.yml entry with git: https://github.com/... even though the marketplace was on corp.ghe.com, poisoning the lockfile with the wrong origin.

Root cause

resolve_marketplace_plugin uses _marketplace_host_needs_explicit_git_path(source.host) to decide whether to build a structured DependencyReference (explicit git: URL + path:). That helper returns False for any GitHub-family host (github.com + *.ghe.com) because shorthand owner/repo[/path] is unambiguous on those hosts -- no GitLab-style nested-group ambiguity.

The flaw: that single condition conflated two orthogonal concerns.

  • Clone-path semantics: do we need explicit git: + path: to disambiguate the clone target? No for GitHub family. ✓
  • Auth-host semantics: does the canonical carry enough info for DependencyReference.parse to recover the correct host? No for *.ghe.com; yes for github.com because it is the documented parse default.

Both concerns were gated on the same return value, so *.ghe.com correctly skipped the structured-ref path but incorrectly also dropped the host hint.

Approach

Backfill the host onto the canonical post-hoc, scoped narrowly. The structured-ref path for GitLab-family hosts is left untouched -- this only fills the gap for the GitHub-family enterprise branch.

A new pure helper _needs_canonical_host_prefix(canonical, host) answers a single question; the call site in resolve_marketplace_plugin adds five lines after the existing structured-ref block:

if (
    dep_ref is None
    and _is_in_marketplace_source(plugin, source)
    and _needs_canonical_host_prefix(canonical, source.host)
):
    canonical = f"{source.host}/{canonical}"

Three guards justify the scope:

  1. dep_ref is None -- the structured-ref path (GitLab family, self-managed FQDN) already carries host via to_canonical(). Don't double-handle.
  2. _is_in_marketplace_source(plugin, source) -- only sources whose host is unambiguously the registered marketplace host get backfilled. Cross-repo dict sources without explicit host qualification are deliberately untouched; treating them as on-host would silently change routing for existing manifests.
  3. _needs_canonical_host_prefix(canonical, host) -- narrows to GitHub-family enterprise hosts (is_github_hostname(host) and host != "github.com") and is idempotent (case-insensitive) against dict sources that already carry a host-qualified repo.

dependency_reference remains None for GitHub-family hosts -- the clone-path shorthand semantics are preserved, only the auth-routing string is corrected.

Tests

New class TestResolveMarketplacePluginGHECloud in tests/unit/marketplace/test_marketplace_resolver.py (7 cases). Each test locks one branch of the helper or one invariant the fix must preserve:

Test Locks
test_relative_source_carries_host_in_canonical Primary bug case; parse round-trip recovers corp.ghe.com
test_dict_github_source_bare_repo_carries_host Dict-source variant of the same in-marketplace fix
test_dict_github_source_host_qualified_repo_not_double_prefixed Idempotent guard against double-prefix
test_dict_github_source_mixed_case_host_qualified_not_double_prefixed Idempotent guard is case-insensitive
test_cross_repo_source_not_prefixed Cross-repo references explicitly out of scope
test_version_spec_override_preserves_host_prefix Ref override stacks on top of host-prefixed canonical
test_github_com_canonical_remains_bare Regression: github.com path unchanged

Validation

End-to-end equivalence check against real AuthResolver (mock marketplace fetch only):

Pre-fix bare canonical This PR Workaround FQDN install
canonical myorg/my-marketplace/plugins/my-plugin corp.ghe.com/myorg/my-marketplace/plugins/my-plugin corp.ghe.com/myorg/my-marketplace/plugins/my-plugin
AuthResolver target host github.com corp.ghe.com corp.ghe.com
HostInfo.kind github ghe_cloud ghe_cloud
clone URL https://github.com/myorg/my-marketplace https://corp.ghe.com/myorg/my-marketplace https://corp.ghe.com/myorg/my-marketplace
apm.yml entry git https://github.com/... https://corp.ghe.com/... https://corp.ghe.com/...

After this change the marketplace path produces byte-identical resolver / auth / lockfile output to the documented workaround (apm install corp.ghe.com/owner/repo/path).

How to test

uv run pytest tests/unit/marketplace/test_marketplace_resolver.py -x

For a manual reproduction:

  1. Register a marketplace on a *.ghe.com host: apm marketplace add --host corp.ghe.com myorg/my-marketplace
  2. Run apm install my-plugin@my-marketplace --verbose
  3. Before this change: Resolved to: myorg/my-marketplace/plugins/my-plugin and Auth resolved: host=github.com (auth fails).
  4. After this change: Resolved to: corp.ghe.com/myorg/my-marketplace/plugins/my-plugin and Auth resolved: host=corp.ghe.com (auth succeeds).

Marketplaces registered on `*.ghe.com` (GHE Cloud) resolved plugin
install auth against `github.com` instead of the registered enterprise
host. The marketplace resolver emitted a canonical without the host
prefix (`owner/repo/path`); `DependencyReference.parse` defaults missing
hosts to `github.com`, mis-routing every downstream auth lookup -- and
poisoning the resulting `apm.yml` entry with the wrong `git:` URL.

`_marketplace_host_needs_explicit_git_path` conflated two orthogonal
concerns. GitHub-family hosts (github.com + *.ghe.com) correctly skip
the GitLab-style structured-ref path because they have no nested-group
ambiguity -- but the same gate also dropped the host hint needed by
`DependencyReference.parse` to recover the correct enterprise host.
`github.com` survived because parse defaults to it; `*.ghe.com` broke.

Backfill the host onto the canonical for in-marketplace sources only,
scoped to GitHub-family enterprise hosts. Idempotent against dict
sources that already carry a host-qualified `repo`. Cross-repo sources
without explicit host qualification stay out of scope -- they are a
separate manifest-author concern and inferring host there would change
existing semantics.

Round-trip stable through `DependencyReference.parse` /
`to_canonical()`, so `apm.yml` lockfile entries now record the
correct enterprise `git:` URL instead of the (silently wrong)
`https://github.com/...` URL produced before the fix.
Copilot AI review requested due to automatic review settings May 12, 2026 13:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes marketplace plugin installs for GHE Cloud (*.ghe.com) hosts by ensuring the resolved canonical reference carries the enterprise host prefix, so downstream DependencyReference.parse() (and auth routing) does not default to github.com (closes #1285).

Changes:

  • Add _needs_canonical_host_prefix() and a scoped host-prefix backfill in resolve_marketplace_plugin() for GitHub-family enterprise hosts.
  • Add a focused unit test suite covering GHE Cloud canonical host prefixing, idempotency, cross-repo non-prefixing, and version ref override behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/apm_cli/marketplace/resolver.py Adds helper + conditional canonical host-prefix backfill for *.ghe.com marketplace resolutions.
tests/unit/marketplace/test_marketplace_resolver.py Adds unit tests validating correct canonical host behavior for GHE Cloud marketplaces and key regressions.

Comment thread src/apm_cli/marketplace/resolver.py
Manifests that put a full URL or SSH SCP shorthand in a dict source's
`repo` field reach `_needs_canonical_host_prefix` via
`_resolve_github_source` (which validates only that `/` is present) and
`_is_in_marketplace_source` (whose normalizer accepts URL/SSH paths).
Before this guard the prefix step produced malformed canonicals like
`corp.ghe.com/https://corp.ghe.com/...` that `DependencyReference.parse`
rejects with `ValueError` -- a regression versus the pre-fix tolerance
where parse recovered host from the URL form natively.

Detect URL/SSH form by `:` in the first slash-split segment of the
canonical (both `https:` and `git@host:owner` carry a colon; bare owner
names and bare host shorthand do not) and return False, leaving the
canonical untouched. Downstream parse already handles both URL and SSH
forms natively for routing, so no host backfill is needed.
@danielmeppiel danielmeppiel added the panel-review Trigger the apm-review-panel gh-aw workflow label May 13, 2026
@github-actions
Copy link
Copy Markdown

APM Review Panel: ship_with_followups

PR #1292 fixes a silent enterprise auth mis-route that blocked *.ghe.com marketplace installs -- a targeted, trust-positive fix with clean security posture and strong unit coverage.

cc @edenfunf -- a fresh advisory pass is ready for your review.

This PR closes a real, user-reported enterprise regression (#1285): marketplace plugins sourced from *.ghe.com hosts were having their canonical silently left bare, causing DependencyReference.parse to default to github.com credentials and fail auth against the enterprise host. Auth-expert and supply-chain-security both confirm the fix is auth-correct and that the host injected into canonical is sourced from the trusted MarketplaceSource registry config -- never from manifest content. The triple gate (dep_ref is None + _is_in_marketplace_source + is_github_hostname excluding github.com) eliminates token cross-contamination risk. Nine unit tests including real DependencyReference.parse boundary assertions give HIGH confidence on the happy path.

Panel signals converge tightly on two follow-up clusters: (1) CHANGELOG + doc surface -- oss-growth-hacker and doc-writer both flag the missing [Unreleased] Fixed entry independently; this is the single highest-value non-code item since enterprise teams who pinned workarounds for #1285 will never discover the fix without it. (2) Cross-repo GHE plugin silent mis-route -- python-architect, devx-ux, and supply-chain-security all independently identify that cross-repo plugins on a *.ghe.com marketplace still emit a bare canonical and silently fall back to github.com auth. All three agree this is pre-existing behaviour, out of scope for this PR, but the absence of any user-facing warning makes it an invisible failure mode that will generate repeat support tickets. A single logger.warning (or structured install-time check) would close the UX gap without changing behaviour. The test-coverage-expert finding of a missing integration-with-fixtures regression trap is evidence-backed (outcome: missing, principles: secure-by-default + governed-by-policy) and ranks above opinion-only recommended findings; this is the right follow-up issue to open, not a blocker for a fix that already has strong unit coverage.

The auth-expert docstring gap (why GHES hosts are intentionally excluded from _needs_canonical_host_prefix) is a low-cost clarification that will prevent future developers from re-introducing the same class of bug. The cli-logging-expert finding of a missing logger.debug on the backfill path directly serves the --verbose diagnostic surface that operators will use when debugging future #1285-class issues.

Aligned with: Secure by Default -- fix ensures enterprise credentials are selected by default for *.ghe.com marketplace sources without any user config change. Portable by Manifest -- transparent to manifest authors, no apm.yml or plugin manifest changes required. Governed by Policy -- auth routing follows the AuthResolver credential hierarchy scoped correctly to the enterprise host; the missing integration-with-fixtures test means this policy contract is not yet machine-verified end-to-end.

Growth signal. This is a quiet enterprise unlock. GHE admins who hit #1285 searched release notes and found nothing -- a CHANGELOG entry with the *.ghe.com host pattern is the single highest-ROI item in this PR's follow-up list. Consider a dedicated "Enterprise" subsection under [Unreleased] as GHE fixes accumulate; it signals intentional enterprise investment to evaluators scanning the changelog before adoption decisions.

Panel summary

Persona B R N Takeaway
Python Architect 0 1 1 Minimal, well-scoped fix; guard logic is correct; one unaddressed cross-repo GHE edge case worth noting; test suite is thorough.
CLI Logging Expert 0 1 1 Backfill path is silent while every comparable resolver branch emits logger.debug; add one debug line so --verbose traces auth routing decisions for *.ghe.com.
DevX UX Expert 0 2 1 Happy-path GHE fix is zero-config and transparent; cross-repo GHE plugins still silently mis-route auth with no actionable error surface.
Supply Chain Security Expert 0 1 1 Fix correctly routes enterprise host auth; host sourced from trusted MarketplaceSource, not manifest content; one residual auth-routing gap for cross-repo plugins.
OSS Growth Hacker 0 1 2 GHE marketplace auth fix is a high-value enterprise unlock -- but missing CHANGELOG entry means enterprise teams won't discover it on upgrade.
Auth Expert 0 1 1 Fix is auth-correct: *.ghe.com canonical host backfill routes enterprise token; GHES/cross-host guards are sound; no token cross-contamination risk.
Doc Writer 0 2 1 CHANGELOG missing #1285 entry; consumer auth doc has no mention of *.ghe.com marketplace auth fallback bug; module docstring is accurate.
Test Coverage Expert 0 1 0 9 unit tests cover helper + backfill thoroughly; integration-tier regression trap for GHE auth routing is missing (floor: integration-with-fixtures for auth resolution).

B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.

Top 5 follow-ups

  1. [OSS Growth Hacker + Doc Writer] Add CHANGELOG [Unreleased] Fixed entry for [BUG] Marketplace install fails for *.ghe.com hosts — auth resolves against github.com instead of registered host #1285 -- Convergent signal from two personas. Enterprise teams who pinned workarounds for [BUG] Marketplace install fails for *.ghe.com hosts — auth resolves against github.com instead of registered host #1285 will never discover this fix without a changelog entry. Suggested copy: "apm marketplace install on *.ghe.com hosts now uses enterprise credentials instead of silently falling back to github.com auth (closes [BUG] Marketplace install fails for *.ghe.com hosts — auth resolves against github.com instead of registered host #1285)". Zero-cost, high-enterprise-trust ROI.
  2. [Test Coverage Expert] Add integration-with-fixtures regression trap for GHE marketplace auth routing -- Evidence-backed missing test (outcome: missing) on a secure-by-default + governed-by-policy surface. The full flow apm install -> resolve_marketplace_plugin -> canonical -> DependencyReference.parse -> auth credential lookup is not exercised by any test in tests/integration/. Suggested file: tests/integration/marketplace/test_ghe_marketplace_install_e2e.py.
  3. [Python Architect + DevX UX Expert + Supply Chain Security Expert] Emit logger.warning for cross-repo GHE plugins that will silently mis-route auth -- Three independent panelists flag this pre-existing gap. When _is_in_marketplace_source returns False for a *.ghe.com marketplace and the canonical is bare owner/repo, the downstream 401 is invisible to the user. A single warning with a host-qualify hint converts a silent failure into an actionable error and prevents repeat [BUG] Marketplace install fails for *.ghe.com hosts — auth resolves against github.com instead of registered host #1285-class support tickets.
  4. [CLI Logging Expert] Add logger.debug on the host-prefix backfill path -- The backfill block is the exact scenario operators will trace with --verbose when debugging *.ghe.com auth failures. Every comparable resolver branch emits a debug line. Suggested: logger.debug("Backfilled enterprise host '%s' onto canonical for %s@%s (auth routing #1285)", source.host, plugin_name, marketplace_name).
  5. [Auth Expert] Add docstring note explaining why GHES (GITHUB_HOST) hosts are intentionally excluded from _needs_canonical_host_prefix -- GHES hosts correctly take the dep_ref path, but a developer reading the function will not know this without an explicit note. Prevents future developers from re-introducing the same class of auth-routing bug.

Architecture

classDiagram
    direction LR
    class resolve_marketplace_plugin {
        <<EntryPoint>>
        +plugin_name str
        +marketplace_name str
        +version_spec str
        +auth_resolver AuthResolver
        +returns MarketplacePluginResolution
    }
    class MarketplacePluginResolution {
        <<ValueObject>>
        +canonical str
        +plugin MarketplacePlugin
        +dependency_reference DependencyReference
    }
    class MarketplaceSource {
        <<ValueObject>>
        +host str
        +owner str
        +repo str
        +branch str
    }
    class MarketplacePlugin {
        <<ValueObject>>
        +name str
        +source str
        +version str
    }
    class DependencyReference {
        <<ValueObject>>
        +host str
        +repo_url str
        +virtual_path str
        +reference str
        +parse(canonical) DependencyReference
        +to_canonical() str
    }
    class _needs_canonical_host_prefix {
        <<Pure>>
        +canonical str
        +host str
        +returns bool
    }
    class _is_in_marketplace_source {
        <<Pure>>
        +plugin MarketplacePlugin
        +source MarketplaceSource
        +returns bool
    }
    class is_github_hostname {
        <<Pure>>
        +hostname str
        +returns bool
    }
    resolve_marketplace_plugin *-- MarketplacePluginResolution : produces
    resolve_marketplace_plugin ..> MarketplaceSource : reads
    resolve_marketplace_plugin ..> MarketplacePlugin : reads
    resolve_marketplace_plugin ..> _is_in_marketplace_source : calls
    resolve_marketplace_plugin ..> _needs_canonical_host_prefix : calls
    resolve_marketplace_plugin ..> DependencyReference : delegates parse
    _needs_canonical_host_prefix ..> is_github_hostname : delegates
    MarketplacePluginResolution o-- DependencyReference : optional
    class _needs_canonical_host_prefix:::touched
    classDef touched fill:#fff3b0,stroke:#d47600
Loading
flowchart TD
    A(["apm install plugin@marketplace"]) --> B["resolve_marketplace_plugin()"]
    B --> C["fetch_or_cache() -- fetch marketplace manifest"]
    C --> D["resolve_plugin_source() -- build bare canonical: owner/repo/path"]
    D --> E{"_marketplace_host_needs_explicit_git_path(source.host)?"}
    E -->|"Yes - GitLab/generic"| F["dep_ref set via _gitlab_in_marketplace_dependency_reference()"]
    E -->|"No - github.com or *.ghe.com"| G["dep_ref = None, canonical stays bare"]
    F --> H{"dep_ref is None?"}
    G --> H
    H -->|"No - GitLab path"| I["skip host backfill"]
    H -->|"Yes"| J{"_is_in_marketplace_source() AND _needs_canonical_host_prefix()?"}
    J -->|"False: github.com, cross-repo, or already host-prefixed"| K["canonical unchanged"]
    J -->|"True: *.ghe.com in-marketplace -- FIX #1285"| L["canonical = source.host/canonical"]
    I --> M{"version_spec override?"}
    K --> M
    L --> M
    M -->|"Yes"| N["canonical = base#version_spec"]
    M -->|"No"| O["MarketplacePluginResolution(canonical, plugin, dep_ref)"]
    N --> O
    O --> P["DependencyReference.parse(canonical) -- routes auth to host in canonical"]
    P --> Q(["Auth at correct GHE host"])
Loading

Recommendation

Ship this PR. The fix is auth-correct, security-positive, and transparent to users -- auth-expert and supply-chain-security both confirm no token cross-contamination risk and a correctly scoped trust boundary. Unit test quality is HIGH with real DependencyReference.parse boundary assertions. The five follow-ups are all non-blocking improvements: the CHANGELOG entry is the highest-urgency item (open a follow-up commit or PR in the same release window); the integration-with-fixtures test should be tracked as a follow-up issue with the secure-by-default label; the cross-repo warning, debug log, and docstring clarification can land in the same minor release. None of these gaps were introduced by this PR, and none represent regressions from the pre-PR state.


Full per-persona findings

Python Architect

  • [recommended] Cross-repo GHE plugins silently misroute auth and the limitation is only documented in a test comment at src/apm_cli/marketplace/resolver.py
    When a *.ghe.com marketplace lists a cross-repo plugin, _is_in_marketplace_source returns False and _needs_canonical_host_prefix is never reached. The bare canonical propagates and DependencyReference.parse defaults to github.com, causing the same auth-routing failure this PR fixes for in-marketplace sources. The test test_cross_repo_source_not_prefixed documents this as intentional but the limitation is invisible to users.
    Suggested: Add a logger.debug or logger.warning in resolve_marketplace_plugin when source.host is a *.ghe.com host and a cross-repo dict source is detected without a host-qualified repo field.

  • [nit] Guard order in _needs_canonical_host_prefix could surface the idempotency check before the colon check at src/apm_cli/marketplace/resolver.py
    The first_segment.lower() != h.lower() idempotency check is the most semantically important guard; currently it is last, after the : check. Reordering is a no-op at runtime but reads top-to-bottom more clearly.

CLI Logging Expert

  • [recommended] Missing logger.debug for host-prefix backfill path at src/apm_cli/marketplace/resolver.py
    The immediately adjacent raw-ref override block emits logger.debug with plugin/marketplace/ref context. The backfill block is the exact scenario operators will need to trace when debugging *.ghe.com auth failures -- yet it is completely silent.
    Suggested: After canonical = f"{source.host}/{canonical}" add: logger.debug("Backfilled enterprise host '%s' onto canonical for %s@%s (auth routing #1285)", source.host, plugin_name, marketplace_name)

  • [nit] warning_handler is not threaded to _needs_canonical_host_prefix callers -- purely cosmetic, no action required.

DevX UX Expert

  • [recommended] Cross-repo GHE plugin: silent auth mis-route with no recovery hint at src/apm_cli/marketplace/resolver.py
    When a GHE marketplace user lists a cross-repo plugin without host qualification, DependencyReference.parse defaults to github.com, auth fails, and the user receives no signal. A one-line warning when _is_in_marketplace_source returns False but source.host is a *.ghe.com host would surface a concrete next action.
    Suggested: Text: "Plugin X source Y is outside the marketplace repository; if it lives on {host}, qualify the repo field as {host}/{repo} for correct authentication."

  • [recommended] No validation or error annotation when canonical is still auth-unresolvable at install time at src/apm_cli/marketplace/resolver.py
    If for any reason the backfill is skipped, the downstream failure is a generic clone/auth error. A structured install-time check comparing dep.host vs source.host would give users one copy-pasteable correction instead of a raw git auth failure.

  • [nit] _needs_canonical_host_prefix docstring is longer than the function body -- accurate but slow to scan; consider a one-line summary + inline comments at key guards.

Supply Chain Security Expert

  • [recommended] Cross-repo plugins on *.ghe.com marketplaces still mis-route auth at src/apm_cli/marketplace/resolver.py
    A cross-repo plugin hosted on the same *.ghe.com instance will still emit a bare canonical, so DependencyReference.parse defaults to github.com credentials. Pre-existing behaviour, not introduced by this PR.

  • [nit] is_github_hostname note about "any FQDN" could mislead future readers -- a one-liner distinguishing *.ghe.com (GHE Cloud) from GHES (arbitrary FQDN) would close the reading gap at src/apm_cli/utils/github_host.py.

OSS Growth Hacker

Auth Expert

  • [recommended] Docstring should clarify why GHES (GITHUB_HOST) hosts are intentionally excluded from _needs_canonical_host_prefix at src/apm_cli/marketplace/resolver.py
    GHES hosts return False from is_github_hostname, which is correct because they already take the dep_ref path. A one-sentence note closes the reading gap and prevents future developers from treating this as a missing case.

  • [nit] String-source shortcut in _is_in_marketplace_source bypasses host validation -- isinstance(s, str) -> return True unconditionally, so a mis-authored manifest entry could receive an unexpected enterprise host prefix. Pre-existing, outside this PR's scope.

Doc Writer

  • [recommended] CHANGELOG [Unreleased] missing entry for [BUG] Marketplace install fails for *.ghe.com hosts — auth resolves against github.com instead of registered host #1285 at CHANGELOG.md
    The [Unreleased] Fixed section has entries but none for this user-visible bug fix.

  • [recommended] consumer/authentication.md has no mention of *.ghe.com marketplace-specific auth routing at docs/src/content/docs/consumer/authentication.md
    The doc covers *.ghe.com auth for direct installs but not marketplace-sourced installs. A user who hits this bug will consult this page and remain confused.

  • [nit] Module docstring: issue reference (#1285) in RST docstring is non-standard and won't render as a hyperlink -- consider dropping or using a .. versionchanged:: directive.

Test Coverage Expert

  • [recommended] No integration-with-fixtures regression trap for GHE marketplace auth routing at tests/integration/marketplace/test_ghe_marketplace_install_e2e.py
    The bug fixed here is user-reported ([BUG] Marketplace install fails for *.ghe.com hosts — auth resolves against github.com instead of registered host #1285). The full flow apm install -> resolve_marketplace_plugin -> canonical -> DependencyReference.parse -> auth credential lookup is not exercised by any test in tests/integration/. Grepped for resolve_marketplace_plugin, MarketplaceSource.*ghe, ghe.*install -- zero hits. The 9 unit tests are HIGH quality (real DependencyReference.parse boundary assertions) but do not certify the end-to-end auth routing promise.
    Proof (missing at integration-with-fixtures): tests/integration/marketplace/test_ghe_marketplace_install_e2e.py -- proves: apm install with a *.ghe.com marketplace source resolves credentials at the enterprise host, not github.com [secure-by-default, governed-by-policy]

This panel is advisory. It does not block merge. Re-apply the
panel-review label after addressing feedback to re-run.

Note

🔒 Integrity filter blocked 2 items

The following items were blocked because they don't meet the GitHub integrity level.

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

Generated by PR Review Panel for issue #1292 · ● 2.9M ·

…low-up

Three non-behavioral additions in response to PR review feedback:

- CHANGELOG `[Unreleased] Fixed` entry so enterprise teams who pinned
  workarounds for the silent github.com auth fallback can discover the
  fix on upgrade (they will not, otherwise -- this is the highest-value
  follow-up surfaced by the review panel).
- `logger.debug` on the host backfill path. The catch-all
  `Resolved %s@%s -> %s` already shows the prefixed canonical at the
  end of resolution, but does not explain WHY the prefix is there.
  Distinguishing "source emitted host-qualified" from "resolver
  backfilled host" matters for operators tracing future microsoft#1285-class
  auth-routing issues with `--verbose`.
- Docstring paragraph on `_needs_canonical_host_prefix` explaining why
  GHES (`GITHUB_HOST` self-managed) is not handled here -- GHES takes
  the structured `dep_ref` path upstream and never reaches this helper.
  Prevents a future developer from extending the helper's condition
  set in a way that conflicts with the upstream structured-ref path.

Cross-repo silent mis-route warning and the integration-test regression
trap suggested by the review panel are tracked as separate follow-up
issues, intentionally not bundled here: the cross-repo warning needs
install-time error-handler placement (a resolver-time always-on warning
would false-positive on intentional github.com cross-host references),
and the integration test needs fixture/marker infrastructure that
inflates this PR's scope without changing the fix's correctness.
@edenfunf edenfunf force-pushed the fix/marketplace-ghe-host-prefix-1285 branch from f3bee6d to 9596af7 Compare May 13, 2026 08:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Marketplace install fails for *.ghe.com hosts — auth resolves against github.com instead of registered host

3 participants